<?php
/**
 * @author spenceryg
 * @date 2023/6/24
 * @time 12:53 上午
 * @focus love & life
 */


namespace app\api1\controller;


use app\common\lib\WxTemplateMsg;
use app\common\model\BaiduToken;
use app\common\model\GptToken;
use app\common\model\RobotsGptToken;
use app\common\model\TemplateMessageRecording;
use GuzzleHttp\Client;
use hg\apidoc\annotation as Apidoc;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;

/**
 * Class Chat
 * @package app\api\controller
 * @Apidoc\Title("聊天")
 */
class Chat extends \app\BaseController
{

    /**
     * @return int|Json|null
     * @Apidoc\Title("聊天")
     * @Apidoc\Param("msg", type="string", default="你好", desc="聊天内容")
     * @Apidoc\Param("robot_id", type="int", default="1", desc="机器人id")
     * @Apidoc\Param("event_name", type="string", default="chat", desc="事件名称")
     */
    public function chat()
    {
        header('X-Accel-Buffering: no');
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache');

        //跨域
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');

        set_time_limit(0); //防止超时
        ob_end_clean();
        ob_implicit_flush(1);


        $event_name = input('event_name', 'chat', 'trim');
        if (!$event_name) {
            return error('事件名称能为空', []);
        }




        $robot_id = input('robot_id');
        if (!$robot_id) {
            return error('id不能为空', []);
        }

        $msg_id = input('msg_id', 0, 'intval');
        if (!$msg_id) {
            return error('msg_id不能为空', []);
        }

        /******************言论审查开始***********************************/
        //数据检验开始，这里审查用户的言论是否符合国家法律规定，这里一定要加上++++++++++++++++++++++++++++++++++++++++++++++++++++++
        $TextModeration=new \app\api\service\TextModerationService();
        if (!$TextModeration->check(json_encode($this->request->param(),JSON_UNESCAPED_UNICODE))) {
            return error($TextModeration->msg);
        }
        /*****************言论审查结束了**********************************/

        $robot = \app\common\model\Robot::find($robot_id);
        $daily_conversation_limit = $robot['daily_conversation_limit'];
        $today = date('Y-m-d');
        $today_conversation_count = \app\common\model\RobotConversationMessage::where('robot_id', $robot_id)->where('create_time', '>=', $today)->count();
        if ($today_conversation_count >= $daily_conversation_limit) {

            $template_msg = TemplateMessageRecording::where('member_id', $robot['member_id'])
                ->where('create_time', '>=', $today)
                ->where('message_template_id', 3)
                ->where('content', 'like', '%(机器人id:'.$robot['id'].')%')
                ->find();


            if(!$template_msg) {
                WxTemplateMsg::send($robot['member_id'], 3, [
                    'thing44' => 'ChatAI套餐超出提醒',
                    'thing36' => $robot['name'].'(机器人id:'.$robot['id'].')',
                    'thing35' => '机器人对话数量超出套餐限制，请升级套餐。'
                ]);
            }

            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $this_str_json = [
                'title' => '今日聊天次数已达上限',
            ];
            $c .= "data:" . json_encode($this_str_json) . PHP_EOL; //推送内容
            echo $c . PHP_EOL;


            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $c .= "data:end" . PHP_EOL; //推送内容
            echo $c . PHP_EOL;


            // 保存机器人回复
            \app\common\model\RobotConversationMessage::where('id', $msg_id)->update([
                'machine_answer' => '今日聊天次数已达上限',
                'answer_time' => time(),
            ]);
            exit;
        }


        $baiduToken = BaiduToken::order('update_time desc')->find();
        $baiduToken->force(true)->save();

//        $gptToken = RobotsGptToken::order('update_time desc')->find();
//        $gptToken->force(true)->save();
//        $openai_api_key = $gptToken['name'];


        $client = new Client([
            'base_uri' => config('transfer.python_address'),
            'timeout' => 80,
        ]);




        try {
            $res = $client->request('post', '/kchat', [
                'stream' => true,
                'json' => [
                    'query' => input('msg'),
//                    'openai_api_key' => $openai_api_key,
                    'persist_directory' => 'dir_' . $robot_id,
                    'robot_id' => $robot_id,
                    'msg_id' => $msg_id,
                    'ak' => $baiduToken->ak,
                    'sk' => $baiduToken->sk,
                ],
            ]);
        } catch (\Exception $e) {
            $msg = $e->getResponse()->getReasonPhrase();
            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $c .= "data:" . $msg . PHP_EOL; //推送内容
            echo $c . PHP_EOL;

            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $c .= "data:end" . PHP_EOL; //推送内容
            echo $c . PHP_EOL;
        }


        $body = $res->getBody();
        $remainder = '';
        $all_res = '';
        while (!$body->eof()) {
            $chunk = $body->read(2);

            // 合并剩余的字符和当前的数据块
            $chunk = $remainder . $chunk;

            // 处理完整的字符
            while (mb_strlen($chunk, 'UTF-8') > 1) {
                $char = mb_substr($chunk, 0, 1, 'UTF-8');
                // 处理每个字符，比如输出或者其他操作
                $c = "event:{$event_name}" . PHP_EOL; //定义事件
                $this_str_json = [
                    'title' => $char,
                ];
                $c .= "data:" . json_encode($this_str_json) . PHP_EOL; //推送内容
                $all_res .= $char;
                echo $c . PHP_EOL;
                // 移除已处理的字符
                $chunk = mb_substr($chunk, 1, null, 'UTF-8');
            }

            // 存储剩余的字符
            $remainder = $chunk;

        }

        $c = "event:{$event_name}" . PHP_EOL; //定义事件
        $this_str_json = [
            'title' => $remainder,
        ];
        $c .= "data:" . json_encode($this_str_json) . PHP_EOL; //推送内容
        echo $c . PHP_EOL;

        $all_res .= $remainder;

        $c = "event:{$event_name}" . PHP_EOL; //定义事件
        $c .= "data:end" . PHP_EOL; //推送内容
        echo $c . PHP_EOL;

        // 保存机器人回复
        \app\common\model\RobotConversationMessage::where('id', $msg_id)->update([
            'machine_answer' => $all_res,
            'answer_time' => time(),
        ]);

        $client->get('/lastToken', [
            'query' => [
                'msg_id' => $msg_id,
            ]
        ]);

        return 1;

    }


    /**
     * @Apidoc\Title("查看引用")
     * @Apidoc\Param("msg", type="string", default="你好", desc="聊天内容")
     * @Apidoc\Param("robot_id", type="int", default="1", desc="机器人id")
     */
    public function reference()
    {
        set_time_limit(0); //防止超时
        $robot_id = input('robot_id', 0, 'intval');
        if (!$robot_id) {
            return error('id不能为空', []);
        }

        $msg_id = input('msg_id', 0, 'intval');
        if (!$msg_id) {
            return error('msg_id不能为空', []);
        }
        $msg = \app\common\model\RobotConversationMessage::where('id', $msg_id)->find();
        $conversation_id = $msg['robot_conversation_id'];

        $client = new Client([
            'base_uri' => config('transfer.python_address'),
            'timeout' => 80,
            'verify' => false,
        ]);

        $gptToken = RobotsGptToken::order('update_time desc')->find();
        $gptToken->force(true)->save();
        $openai_api_key = $gptToken['name'];


        $json = [
            'query' => input('query'),
            'robot_id' => $robot_id,
//            'openai_api_key' => $openai_api_key,
            'openai_api_key' => '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',
            'msg_id' => $msg_id,
        ];


        try {
            $res = $client->post('/reference', [
                'json' => $json,
            ]);
        } catch (\Exception $e) {
            return error($e->getMessage());
        }


        $body = $res->getBody()->getContents();

        $body = json_decode($body, true);

        $references = [];
        foreach ($body['docs'] as $reference) {
            $references[] = [
                'url' => $reference[0]['metadata']['original_name'],
                'score' => $reference[1],
            ];
        }
        $event = [];
        $similarity = 0.00;
        $event_id = 0;
        if ($body['event']) {
            $event_id = $body['event'][0][0]['metadata']['original_name'];
            $event = \app\common\model\RobotEvent::find($event_id);
            $similarity = $body['event'][0][1];
        }

        $msg_update = [
            'referenced_files' => $references,
        ];
        if ($event) {
            $msg_update['event_id'] = $event['id'];
            $msg_update['event_details'] = $event;
            $msg_update['similarity'] = $similarity;
        }

        // 同一个事件， 一个对话只能触发一次
        $hasEvent = \app\common\model\RobotConversationMessage::where('robot_conversation_id', $conversation_id)
            ->where('event_id', $event_id)
            ->where('is_event_submission', 1)
            ->find();
        if ($hasEvent) {
            $event = [];
            $msg_update['is_event_submission'] = -1;
        }


        \app\common\model\RobotConversationMessage::where('id', $msg_id)->update($msg_update);


        return success('', ['references' => $references, 'event' => $event, 'origin_events' => $body['event']]);

    }


    /**
     * 创建对话
     * @Apidoc\Title ("创建对话")
     * @Apidoc\Param("robot_id", type="int", default="1", desc="机器人id")
     * @Apidoc\Param("query", type="string", default="你好", desc="对话名称")
     */
    public function createConversation()
    {
        $robot_id = input('robot_id', 0, 'intval');
        if (!$robot_id) {
            return error('id不能为空', []);
        }
        $query = input('query');
        if (!$query) {
            return error('query不能为空', []);
        }
        $name = mb_substr($query, 0, 10, 'UTF-8');
        $conversation = \app\common\model\RobotConversation::create([
            'robot_id' => $robot_id,
            'name' => $name,
        ]);
        return success('', $conversation);
    }


    /**
     * @return Json|null
     * @Apidoc\Title ("创建对话消息")
     * @Apidoc\Param("robot_id", type="int", default="1", desc="机器人id")
     * @Apidoc\Param("conversation_id", type="int", default="1", desc="对话id")
     * @Apidoc\Param("user_questions", type="string", default="你好", desc="对话内容")
     */
    public function createMessage()
    {
        $robot_id = input('robot_id', 0, 'intval');
        if (!$robot_id) {
            return error('id不能为空', []);
        }
        $conversation_id = input('conversation_id', 0, 'intval');
        if (!$conversation_id) {
            return error('conversation_id不能为空', []);
        }
        $query = input('user_questions');
        if (!$query) {
            return error('问题不能为空', []);
        }
        $TextModeration=new \app\api\service\TextModerationService();
        if (!$TextModeration->check(json_encode(request()->param(),JSON_UNESCAPED_UNICODE))) {
            return error($TextModeration->msg);
        }
        $message = \app\common\model\RobotConversationMessage::create([
            'robot_id' => $robot_id,
            'robot_conversation_id' => $conversation_id,
            'user_questions' => $query,
        ]);
        return success('', $message);
    }


    /**
     * @Apidoc\Title("点赞")
     * @Apidoc\Param("conversation_id", type="int", default="1", desc="对话id")
     */
    public function like()
    {
        $conversation_id = input('conversation_id', 0, 'intval');
        if (!$conversation_id) {
            return error('msg_id不能为空', []);
        }
        $msg = \app\common\model\RobotConversationMessage::find($conversation_id);
        if ($msg->is_positive_reviews == 0) {
            $msg->is_positive_reviews = 1;
            $msg->save();
            \app\common\model\Robot::where('id', $msg->robot_id)
                ->inc('number_of_likes')
                ->update();
            return success('点赞成功');
        } elseif ($msg->is_positive_reviews == 1) {
            return success('点赞成功');
        } else {
            $msg->is_positive_reviews = 1;
            $msg->save();
            \app\common\model\Robot::where('id', $msg->robot_id)
                ->inc('number_of_likes')
                ->dec('number_of_unlikes')
                ->update();
            return success('点赞成功');
        }

    }

    /**
     * @Apidoc\Title("点踩")
     * @Apidoc\Param("conversation_id", type="int", default="1", desc="对话id")
     */
    public function unlike()
    {
        $conversation_id = input('conversation_id', 0, 'intval');
        if (!$conversation_id) {
            return error('msg_id不能为空', []);
        }
        $msg = \app\common\model\RobotConversationMessage::find($conversation_id);
        if ($msg->is_positive_reviews == 0) {
            $msg->is_positive_reviews = -1;
            $msg->save();
            \app\common\model\Robot::where('id', $msg->robot_id)
                ->inc('number_of_unlikes')
                ->update();
            return success('点踩成功');
        } elseif ($msg->is_positive_reviews == 1) {
            $msg->is_positive_reviews = -1;
            $msg->save();
            \app\common\model\Robot::where('id', $msg->robot_id)
                ->inc('number_of_unlikes')
                ->dec('number_of_likes')
                ->update();
            return success('点踩成功');
        } else {
            return success('点踩成功');
        }
    }


    /**
     * @return Json|null
     * @Apidoc\Title ("提交事件")
     * @Apidoc\Param("msg_id", type="int", default="1", desc="消息id")
     * @Apidoc\Param("submission", type="string", default="你好", desc="提交内容")
     */
    public function userSubmitEvent()
    {
        $msg_id = input('msg_id', 0, 'intval');
        if (!$msg_id) {
            return error('msg_id不能为空', []);
        }
        $msg = \app\common\model\RobotConversationMessage::find($msg_id);
        $msg->is_event_submission = 1;
        $submission = input('user_submission');
        foreach ($submission as $key => $value) {
            if (!$value) {
                return error('表单字段:' . $key . ' ,不能为空!');
            }
        }
        $msg->user_submission = $submission;
        $msg->save();
        return success('提交成功');
    }

    /**
     * getById
     * @Apidoc\Method("post")
     * @Apidoc\Title ("获取对话详情")
     * @Apidoc\Query(key="id", rule="int", default="", desc="id")
     */
    public function getConversationById()
    {
        $id = input('id', 0, 'intval');
        $res = \app\common\model\RobotConversation::find($id);
        $messages = \app\common\model\RobotConversationMessage::where('robot_conversation_id', $id)
            ->order('id', 'asc')->select()
            ->each(function ($item) {
                $item['answer_time_str'] = date('Y-m-d H:i:s', $item['answer_time']);
                $item['isActive'] = false;
                return $item;
            });
        return success('ok', [
            'conversation' => $res,
            'messages' => $messages
        ]);
    }

    /**
     * @Apidoc\Title("根据code获取机器人")
     */
    public function getRobotByCode()
    {
        //跨域

        $code = input('code', '', 'trim');
        if (!$code) {
            return error('code不能为空');
        }
        $domain = input('domain', '', 'trim');
        $robot = \app\common\model\Robot::where('code', $code)->find();
        if ($robot) {
            if ($robot->domain) {
                if (!in_array($domain, $robot->domain)) {
                    return error('域名不匹配');
                }
            }
        }else{
            return error('机器人不存在');
        }

        if($robot->is_enable_js==0){
            return error('机器人已经关闭');
        }
        return success('', $robot);

    }


    /**
     * @Apidoc\Title("根据code和密码获取机器人")
     */
    public function getRobotByCodeAndPwd()
    {
        //跨域

        $code = input('code', '', 'trim');
        if (!$code) {
            return error('code不能为空');
        }
        $password = input('password', '', 'trim');
        $robot = \app\common\model\Robot::where('code', $code)
            ->where('share_password', $password)
            ->find();
        if ($robot) {
            if($robot->is_enable_sharing==0){
                return error('机器人已经关闭,请联系分享人！');
            }
        }else{
            return error('机器人不存在或密码错误！');
        }

        return success('', $robot);

    }


    /**
     * @Apidoc\Title("根据key获取机器人聊天")
     */
    public function apiChat()
    {
        header('X-Accel-Buffering: no');
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache');

        //跨域
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');

        set_time_limit(0); //防止超时
        ob_end_clean();
        ob_implicit_flush(1);

        $event_name = 'chat';

        if(!input('msg')){
            return error('msg不能为空');
        }

        $key = input('key', '', 'trim');
        if (!$key) {
            return error('key不能为空');
        }
        $robot_id = \app\common\model\RobotApi::where('key', $key)->value('robot_id');
        if(!$robot_id){
            return error('key错误');
        }
        $robot = \app\common\model\Robot::find($robot_id);
        $daily_conversation_limit = $robot['daily_conversation_limit'];
        $today = strtotime(date('Y-m-d'));
        $today_conversation_count = \app\common\model\RobotConversationMessage::where('robot_id', $robot_id)->where('create_time', '>=', $today)->count();
        if ($today_conversation_count >= $daily_conversation_limit) {

            $template_msg = TemplateMessageRecording::where('member_id', $robot['member_id'])
                ->where('create_time', '>=', $today)
                ->where('message_template_id', 3)
                ->where('content', 'like', '%(机器人id:'.$robot['id'].')%')
                ->find();



            if(!$template_msg) {
                WxTemplateMsg::send($robot['member_id'], 3, [
                    'thing44' => 'ChatAI套餐超出提醒',
                    'thing36' => $robot['name'].'(机器人id:'.$robot['id'].')',
                    'thing35' => '机器人对话数量超出套餐限制，请升级套餐。'
                ]);
            }

            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $this_str_json = [
                'title' => '今日聊天次数已达上限',
            ];
            $c .= "data:" . json_encode($this_str_json) . PHP_EOL; //推送内容
            echo $c . PHP_EOL;


            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $c .= "data:end" . PHP_EOL; //推送内容
            echo $c . PHP_EOL;
            exit;

        }





        $conversion = \app\common\model\RobotConversation::where('robot_id', $robot_id)
            ->where('name', $key.'__api对话')
            ->find();
        if(!$conversion){
            $conversion = \app\common\model\RobotConversation::create(['name'=>$key.'__api对话','robot_id'=>$robot_id]);
        }
        $conversion_id = $conversion->id;

        $msg = \app\common\model\RobotConversationMessage::create([
                'user_questions' => input('msg'),
                'robot_conversation_id' => $conversion_id,
                'robot_id' => $robot_id,
            ]);

        $msg_id = $msg->id;



        $gptToken = RobotsGptToken::order('update_time desc')->find();
        $gptToken->force(true)->save();
        $openai_api_key = $gptToken['name'];


        $client = new Client([
            'base_uri' => config('transfer.python_address'),
            'timeout' => 80,
        ]);


        try {
            $res = $client->request('post', '/kchatApi', [
                'stream' => true,
                'json' => [
                    'query' => input('msg'),
                    'openai_api_key' => $openai_api_key,
                    'persist_directory' => 'dir_' . $robot_id,
                    'robot_id' => $robot_id,
                    'msg_id' => $msg_id,
                ],
            ]);
        } catch (\Exception $e) {
            $msg = $e->getResponse()->getReasonPhrase();
            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $c .= "data:" . $msg . PHP_EOL; //推送内容
            echo $c . PHP_EOL;

            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $c .= "data:end" . PHP_EOL; //推送内容
            echo $c . PHP_EOL;
        }


        $body = $res->getBody();
        $remainder = '';
        $all_res = '';
        while (!$body->eof()) {
            $chunk = $body->read(2);

            // 合并剩余的字符和当前的数据块
            $chunk = $remainder . $chunk;

            // 处理完整的字符
            while (mb_strlen($chunk, 'UTF-8') > 1) {
                $char = mb_substr($chunk, 0, 1, 'UTF-8');
                // 处理每个字符，比如输出或者其他操作
                $c = "event:{$event_name}" . PHP_EOL; //定义事件
                $this_str_json = [
                    'title' => $char,
                ];
                $c .= "data:" . json_encode($this_str_json) . PHP_EOL; //推送内容
                $all_res .= $char;
                echo $c . PHP_EOL;
                // 移除已处理的字符
                $chunk = mb_substr($chunk, 1, null, 'UTF-8');
            }

            // 存储剩余的字符
            $remainder = $chunk;

        }

        $c = "event:{$event_name}" . PHP_EOL; //定义事件
        $this_str_json = [
            'title' => $remainder,
        ];
        $c .= "data:" . json_encode($this_str_json) . PHP_EOL; //推送内容
        echo $c . PHP_EOL;

        $all_res .= $remainder;

        $c = "event:{$event_name}" . PHP_EOL; //定义事件
        $c .= "data:end" . PHP_EOL; //推送内容
        echo $c . PHP_EOL;

        // 保存机器人回复
        \app\common\model\RobotConversationMessage::where('id', $msg_id)->update([
            'machine_answer' => $all_res,
            'answer_time' => time(),
        ]);

        $client->get('/lastToken', [
            'query' => [
                'msg_id' => $msg_id,
            ]
        ]);

        return 1;
    }











}
